﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using WifiScanner.Classes;
using NativeWifi;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Timers;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing.Imaging;
using System.Drawing;

namespace WifiScanner
{

    public partial class MainWindow : Window
    {
        LinkedList<Wifi> wifiNetworks = new LinkedList<Wifi>();
        WlanClient client = new WlanClient();
        ScrollViewer sv = new ScrollViewer();
        Radar r;
        System.Windows.Controls.Image image1;
        Grid sideGrid = new Grid();
        Grid sideGrid2 = new Grid();
        Grid sideGrid3 = new Grid();
        Logger logger = new Logger();
        int devicesCounter = 0;
        const int MAX_DEVICES = 1000;
        /*
         * 
         *  Hostnames uitgeschakeld in IPInfo.cs
         * 
         *
         */

        /// <summary>
        /// Inistialisatie van het hoofdvenster.
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
            Width = 616;

            // Wifi netwerken ophalen + de sideGrid toevoegen aan de canvas
            try
            {
                getWifiNetworks();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Er is een fout opgetreden tijdens het scannen van draadloze netwerken.\nMogelijk is een inactieve netwerkadapter de oorzaak.", "Fout bij het opstarten", MessageBoxButton.OK, MessageBoxImage.Error);
                logger.log(ex.ToString());
                Application.Current.Shutdown();
            }

            // Elke functie heeft een eigen sidegrid
            canvas.Children.Add(sideGrid3);
            canvas.Children.Add(sideGrid2);
            canvas.Children.Add(sideGrid);
        }

        /// <summary>
        /// Extra informatie ivm het verbonden netwerk weergeveven.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnInformation_Click(object sender, RoutedEventArgs e)
        {
            // Canvas + Window groter maken
            this.Width = 900;
            canvas.Width = 900;

            // Controls op de sidegrid verwijderen, lengte en breedte instellen + margin (plaats van de grid)
            sideGrid.Children.Clear();
            sideGrid2.Children.Clear();
            sideGrid3.Children.Clear();
            sideGrid.Width = 300;
            sideGrid.Height = 300;
            sideGrid.Margin = new Thickness(565, 35, 0, 30);

            // Declaratie van controls
            Label lblSSID = new Label();
            Label lblMAC = new Label();
            Label lblDBM = new Label();
            Label lblChannel = new Label();
            Label lblName = new Label();
            Label lblDesc = new Label();
            Label lblAuth = new Label();
            Label lblCipher = new Label();
            Label lblProfile = new Label();
            Label lblExtra = new Label();
            TextBox txtSSID = new TextBox();
            TextBox txtMAC = new TextBox();
            TextBox txtDBM = new TextBox();
            TextBox txtChannel = new TextBox();
            TextBox txtName = new TextBox();
            TextBox txtDesc = new TextBox();
            TextBox txtAuth = new TextBox();
            TextBox txtCipher = new TextBox();
            TextBox txtProfile = new TextBox();

            // Instellen van controls
            lblSSID.Content = "SSID:";
            lblMAC.Content = "Mac-adres";
            lblDBM.Content = "dBm (signaalsterkte):";
            lblChannel.Content = "Kanaal:";
            lblName.Content = "Interface:";
            lblDesc.Content = "Interface omschrijving:";
            lblAuth.Content = "Auth:";
            lblCipher.Content = "Cipher:";
            lblProfile.Content = "Profile:";
            lblExtra.Content = "Eigenschappen van het netwerk";
            lblExtra.FontWeight = FontWeights.ExtraBold;

            // Ophalen van de extra informatie van de huidige connectie
            try {
                Dictionary<String, String> info = getConnectionInfo();
                txtSSID.Text = info["SSID"];
                txtMAC.Text = info["MAC"];
                txtDBM.Text = info["DBM"];
                txtChannel.Text = info["channel"];
                txtName.Text = info["name"];
                txtDesc.Text = info["desc"];
                txtAuth.Text = info["auth"];
                txtCipher.Text = info["cipher"];
                txtProfile.Text = info["profile"];
            }
            catch (Exception ex)
            {
                MessageBox.Show("Fout tijdens het ophalen van adapter informatie.", "Fout", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                logger.log(ex.ToString());
            }

            // Zorgen dat er niets bewerkt kan worden
            txtSSID.IsReadOnly = true;
            txtMAC.IsReadOnly = true;
            txtDBM.IsReadOnly = true;
            txtChannel.IsReadOnly = true;
            txtName.IsReadOnly = true;
            txtDesc.IsReadOnly = true;
            txtAuth.IsReadOnly = true;
            txtCipher.IsReadOnly = true;
            txtProfile.IsReadOnly = true;

            // Marges instellen omdat anders de tekstvakken tegen elkaar plakken
            txtSSID.Margin = new Thickness(0, 0, 0, 5);
            txtMAC.Margin = new Thickness(0, 0, 0, 5);
            txtDBM.Margin = new Thickness(0, 0, 0, 5);
            txtChannel.Margin = new Thickness(0, 0, 0, 5);
            txtName.Margin = new Thickness(0, 0, 0, 5);
            txtDesc.Margin = new Thickness(0, 0, 0, 5);
            txtAuth.Margin = new Thickness(0, 0, 0, 5);
            txtCipher.Margin = new Thickness(0, 0, 0, 5);
            txtProfile.Margin = new Thickness(0, 0, 0, 5);

            // Kolommen en rijen aanmaken
            sideGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(130) });
            sideGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(170) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });
            sideGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(28) });

            // Toevoegen van controls
            sideGrid.Children.Add(lblSSID);
            sideGrid.Children.Add(lblMAC);
            sideGrid.Children.Add(lblDBM);
            sideGrid.Children.Add(lblChannel);
            sideGrid.Children.Add(lblName);
            sideGrid.Children.Add(lblDesc);
            sideGrid.Children.Add(lblAuth);
            sideGrid.Children.Add(lblCipher);
            sideGrid.Children.Add(lblProfile);
            sideGrid.Children.Add(txtSSID);
            sideGrid.Children.Add(txtMAC);
            sideGrid.Children.Add(txtDBM);
            sideGrid.Children.Add(txtChannel);
            sideGrid.Children.Add(txtName);
            sideGrid.Children.Add(txtDesc);
            sideGrid.Children.Add(txtAuth);
            sideGrid.Children.Add(txtCipher);
            sideGrid.Children.Add(txtProfile);
            sideGrid.Children.Add(lblExtra);

            // Plaatsing
            Grid.SetRow(lblExtra, 0);
            Grid.SetColumnSpan(lblExtra, 2);
            Grid.SetColumn(lblExtra, 0);
            Grid.SetRow(lblSSID, 1);
            Grid.SetColumn(lblSSID, 0);
            Grid.SetRow(txtSSID, 1);
            Grid.SetColumn(txtSSID, 1);
            Grid.SetRow(lblMAC, 2);
            Grid.SetColumn(lblMAC, 0);
            Grid.SetRow(txtMAC, 2);
            Grid.SetColumn(txtMAC, 1);
            Grid.SetRow(lblDBM, 3);
            Grid.SetColumn(lblDBM, 0);
            Grid.SetRow(txtDBM, 3);
            Grid.SetColumn(txtDBM, 1);
            Grid.SetRow(lblChannel, 4);
            Grid.SetColumn(lblChannel, 0);
            Grid.SetRow(txtChannel, 4);
            Grid.SetColumn(txtChannel, 1);
            Grid.SetRow(lblName, 5);
            Grid.SetColumn(lblName, 0);
            Grid.SetRow(txtName, 5);
            Grid.SetColumn(txtName, 1);
            Grid.SetRow(lblDesc, 6);
            Grid.SetColumn(lblDesc, 0);
            Grid.SetRow(txtDesc, 6);
            Grid.SetColumn(txtDesc, 1);
            Grid.SetRow(lblAuth, 7);
            Grid.SetColumn(lblAuth, 0);
            Grid.SetRow(txtAuth, 7);
            Grid.SetColumn(txtAuth, 1);
            Grid.SetRow(lblCipher, 8);
            Grid.SetColumn(lblCipher, 0);
            Grid.SetRow(txtCipher, 8);
            Grid.SetColumn(txtCipher, 1);
            Grid.SetRow(lblProfile, 9);
            Grid.SetColumn(lblProfile, 0);
            Grid.SetRow(txtProfile, 9);
            Grid.SetColumn(txtProfile, 1);

            // Radar niet meer zichtbaar dus update niet meer nodig, unsubscribe
            if (r != null)
            {
                r.ImageUpdate -= new ImageUpdateHandler(imageUpdate);
            }
        }

        /// <summary>
        /// Radar activeren.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRadar_Click(object sender, RoutedEventArgs e)
        {
            // Als er 2 keer op de knop word geduwd zijn er problemen. 
            if (canvas.Width == 1000) // --> workaround
            {
                return;
            }

            Random rnd = new Random();
            image1 = new System.Windows.Controls.Image();

            #region Windowing..

            // Canvas + Window groter maken
            this.Width = 1000;
            canvas.Width = 1000;

            // Controls op de sidegrid verwijderen, lengte en breedte instellen + margin (plaats van de grid)
            sideGrid.Children.Clear();
            sideGrid2.Children.Clear();
            sideGrid3.Children.Clear();

            sideGrid3.Width = 428;
            sideGrid3.Height = 358;
            sideGrid3.Margin = new Thickness(565, 35, 0, 40);

            image1.Width = 428;
            image1.Height = 358;

            sideGrid3.Children.Add(image1);

            #endregion

            int i = 1;
            RadarItem rI;
            r = new Radar((int)image1.Width);
            r.DrawScanInterval = 60; // in millisecs
            r.DrawScanLine = true;

            foreach (Wifi w in wifiNetworks)
            {
                int DBm = Int16.Parse(w.Dbm.Substring(0, w.Dbm.Length - 3)) * -1;
                rI = new CircleRadarItem(i, 8, rnd.Next(360), 100 - DBm, w, Color.FromArgb(255, 255, 255));
                r.AddItem(rI);
                i++;
            }

            r.ImageUpdate += new ImageUpdateHandler(imageUpdate);

        }

        /// <summary>
        /// Ophalen van clients en weergeven in een treeview.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRetrieveClients_Click(object sender, RoutedEventArgs e)
        {
            // Canvas + Window groter maken
            this.Width = 800;
            canvas.Width = 800;

            // Controls op de sidegrid verwijderen, lengte en breedte instellen + margin (plaats van de grid)
            sideGrid.Children.Clear();
            sideGrid2.Children.Clear();
            sideGrid3.Children.Clear();
            sideGrid2.Width = 200;
            sideGrid2.Height = 300;
            sideGrid2.Margin = new Thickness(565, 40, 0, 30);

            // Aanmaken treeview + vullen + scrollview toevoegen aan sidegrid
            try
            {
                TreeViewWithIcons tv = new TreeViewWithIcons();
                Dictionary<String, String> info = getConnectionInfo();
                getClients(tv, info["SSID"]);
                tv.Width = 200;
                tv.Margin = new Thickness(0, 0, 0, 5);
                sideGrid2.Children.Add(sv);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Fout tijdens het ophalen van adapter informatie.", "Fout", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                logger.log(ex.ToString());
            }

            // Radar niet meer zichtbaar dus update niet meer nodig, unsubscribe
            if (r != null)
            {
                r.ImageUpdate -= new ImageUpdateHandler(imageUpdate);
            }
        }

        /// <summary>
        /// Maakt het formulier verplaatsbaar.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Border_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragMove();
        }

        /// <summary>
        /// Sluit de applicatie.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        /// <summary>
        /// Zoekt opnieuw naar wifi netwerken.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRefresh_Click(object sender, RoutedEventArgs e)
        {
            try {
                // Wifi netwerken ophalen
                getWifiNetworks();

                // Alle bollekes terug wit maken op de radar
                if (r != null)
                {
                    for (int i = 0; i < r.radarItemList.Count; i++)
                    {
                        r.radarItemList[i].Kleur = Color.White;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Er is een fout opgetreden tijdens het scannen van draadloze netwerken.\nMogelijk is een inactieve netwerkadapter de oorzaak.", "Fout bij het opstarten", MessageBoxButton.OK, MessageBoxImage.Error);
                logger.log(ex.ToString());
                Application.Current.Shutdown();
            }
        }

        /// <summary>
        /// Ophalen van gegevens van het verbonden netwerk.
        /// </summary>
        /// <returns>Dictionary met de gegevens</returns>
        private Dictionary<String, String> getConnectionInfo()
        {
            Dictionary<string, string> info = new Dictionary<string, string>();
            foreach (WlanClient.WlanInterface wlanInterface in client.Interfaces)
            {
                Wlan.Dot11Ssid ssid = wlanInterface.CurrentConnection.wlanAssociationAttributes.dot11Ssid;
                Byte[] bssid = wlanInterface.CurrentConnection.wlanAssociationAttributes.dot11Bssid;
                String auth = wlanInterface.CurrentConnection.wlanSecurityAttributes.dot11AuthAlgorithm.ToString();
                String cipher = wlanInterface.CurrentConnection.wlanSecurityAttributes.dot11CipherAlgorithm.ToString();
                String profile = wlanInterface.CurrentConnection.profileName;
                String channel = wlanInterface.Channel.ToString();
                String name = wlanInterface.InterfaceName;
                String desc = wlanInterface.InterfaceDescription;
                String rssi = wlanInterface.RSSI.ToString();
                info.Add("SSID", getStringForSSID(ssid));
                info.Add("MAC", getStringForMAC(bssid));
                info.Add("auth", auth);
                info.Add("cipher", cipher);
                info.Add("profile", profile);
                info.Add("channel", channel);
                info.Add("name", name);
                info.Add("desc", desc);
                info.Add("DBM", rssi);
            }
            return info;
        }

        /// <summary>
        /// Haalt de gevonden wifi netwerken op.
        /// </summary>
        private void getWifiNetworks()
        {
            HashSet<String> hs = new HashSet<String>();
            wifiNetworks.Clear();
            ListView.Items.Clear();

            // Voor elke interface
            foreach (WlanClient.WlanInterface wlanIface in client.Interfaces)
            {
                // MAC adressen opvragen van de wifi's
                Wlan.WlanBssEntry[] wlanBssEntries = wlanIface.GetNetworkBssList();

                // Voor elk gevonden MAC adres
                foreach (Wlan.WlanBssEntry wlanBssEntry in wlanBssEntries)
                {
                    // Wifi object aanmaken
                    Wifi wifiNetwork = new Wifi() { SSID = getStringForSSID(wlanBssEntry.dot11Ssid), MAC = getStringForMAC(wlanBssEntry.dot11Bssid), Dbm = wlanBssEntry.rssi + " dBm", Quality = wlanBssEntry.linkQuality.ToString() + "%" };
                    
                    // Controle op naamloze wifi's
                    if (wifiNetwork.SSID == "") { wifiNetwork.SSID = "Ander netwerk"; }

                    // Als het een uniek netwerk is, toevoegen aan de list
                    if (!hs.Contains(wifiNetwork.SSID))
                    {
                        hs.Add(wifiNetwork.SSID);
                        wifiNetworks.AddLast(wifiNetwork);
                        ListView.Items.Add(wifiNetwork);
                    }
                }
            }
        }

        /// <summary>
        /// SSID omzetten naar leesbare vorm
        /// </summary>
        /// <param name="ssid"></param>
        /// <returns></returns>
        static string getStringForSSID(Wlan.Dot11Ssid ssid)
        {
            return Encoding.ASCII.GetString(ssid.SSID, 0, (int)ssid.SSIDLength);
        }

        /// <summary>
        /// MAC omzetten naar leesbare vorm
        /// </summary>
        /// <param name="macAddr"></param>
        /// <returns></returns>
        static string getStringForMAC(byte[] macAddr)
        {
            // bytes-array omzetten naar MAC adres
            var macAddrLen = (uint)macAddr.Length;
            var str = new string[(int)macAddrLen];
            for (int i = 0; i < macAddrLen; i++)
            {
                str[i] = macAddr[i].ToString("x2");
            }
            string mac = string.Join("", str);

            // MAC adres, de typische MAC-adres opmaak geven.
            String finalMac = "";
            for (int i = 0; i < mac.Length; i += 2)
            {
                String two = mac.Substring(i, 2);
                if (two != null && two.Length > 0)
                {
                    finalMac += two.ToUpper() + "-";
                }
            }
            finalMac = finalMac.Substring(0, finalMac.Length - 1);
            return finalMac;
        }

        /// <summary>
        /// Ophalen van clients op basis van het SSID van het netwerk. De clients worden in een treeview gezet
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="ssid"></param>
        private void getClients(TreeViewWithIcons tv, String ssid)
        {
            var ipinfo = IPInfo.GetIPInfo();
            sv.CanContentScroll = true;
            sv.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
            
            // Voor elke client
            foreach (IPInfo s in ipinfo)
            {
                devicesCounter++;
                if (devicesCounter < MAX_DEVICES)
                {
                    // Childs
                    TreeViewWithIcons IP = new TreeViewWithIcons();
                    TreeViewWithIcons MAC = new TreeViewWithIcons();
                    IP.HeaderText = s.IPAddress;
                    MAC.HeaderText = s.MacAddress.ToUpper();
                    IP.Icon = createImage("client_tv.ico");
                    MAC.Icon = createImage("hardwareSmall.png");
                    IP.Items.Add(MAC);
                    tv.Items.Add(IP);
                }
                else
                {
                    break;
                }
            }
            
            // Head
            tv.HeaderText = ssid;
            tv.Icon = createImage("network_tv.ico");
            tv.VerticalAlignment = VerticalAlignment.Stretch;
            sv.Content = tv;
        }

        /// <summary>
        /// Omzetten van de icons naar bitmapafbeeldingen
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public BitmapImage createImage(string path)
        {
            var uriSource = new Uri(@"/WifiScanner;component/Images/"+ path, UriKind.Relative);
            BitmapImage myBitmapImage = new BitmapImage();
            myBitmapImage.BeginInit();
            myBitmapImage.UriSource = uriSource;
            myBitmapImage.EndInit();
            return myBitmapImage;
        }

        /// <summary>
        /// Functie voor het hertekenen van de radar.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void imageUpdate(object sender, ImageUpdateEventArgs e)
        {
            image1.Source = convertImage(e.Image);
        }

        /// <summary>
        /// Afbeelding naar bitmap omzetten.
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        private static BitmapImage convertImage(System.Drawing.Image img)
        {
            BitmapImage bi = new BitmapImage();
            MemoryStream ms = new MemoryStream();

            bi.BeginInit();

            img.Save(ms, ImageFormat.Bmp);
            ms.Seek(0, SeekOrigin.Begin);
            bi.StreamSource = ms;

            bi.EndInit();

            return bi;
        }

        /// <summary>
        /// Veranderen van de listview kleurt het bijhorende bolletje.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (this.Width == 1000 && e.AddedItems.Count != 0) //lolwtfhacks
            {
                Wifi w = (Wifi) e.AddedItems[0];
                foreach (CircleRadarItem i in r.radarItemList)
                {
                    if (i.wifi.SSID == w.SSID)
                    {
                        r.radarItemList[r.radarItemList.IndexOf(i)].Kleur = Color.Red;
                        for (int j = 0; j < r.radarItemList.Count; j++)
                        {
                            if (j != r.radarItemList.IndexOf(i))
                            {
                                r.radarItemList[j].Kleur = Color.White;
                            }
                        }
                        break;
                    }
                }
            }
        }
    }
}
